package org.totoro.concurrent.sync.mark_word;

import lombok.Builder;
import lombok.Data;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import org.openjdk.jol.vm.VirtualMachine;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * |------------------------------------------------------------------------------|--------------------|
 * |                                  Mark Word (64 bits)                         |       State        |
 * |------------------------------------------------------------------------------|--------------------|
 * | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |       Normal       | (01)无锁
 * |------------------------------------------------------------------------------|--------------------|
 * | thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |       Biased       | (01)偏向锁
 * |------------------------------------------------------------------------------|--------------------|
 * |                       ptr_to_lock_record:62                         | lock:2 | Lightweight Locked | (00)轻量级锁
 * |------------------------------------------------------------------------------|--------------------|
 * |                     ptr_to_heavyweight_monitor:62                   | lock:2 | Heavyweight Locked | (10)重量级锁
 * |------------------------------------------------------------------------------|--------------------|
 * |                                                                     | lock:2 |    Marked for GC   | (11)GC标记
 * |------------------------------------------------------------------------------|--------------------|
 * <p>
 *
 * <p>
 * |-------------------------------------------------------|--------------------|
 * |                  Mark Word (32 bits)                  |       State        |
 * |-------------------------------------------------------|--------------------|
 * | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
 * |-------------------------------------------------------|--------------------|
 * |  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
 * |-------------------------------------------------------|--------------------|
 * |               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
 * |-------------------------------------------------------|--------------------|
 * |               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
 * |-------------------------------------------------------|--------------------|
 * |                                              | lock:2 |    Marked for GC   |
 * |-------------------------------------------------------|--------------------|
 *
 * @author daocr
 * @date 2020/3/15
 */
public abstract class MarkWordStrategyAbs implements MarkWordStrategy {

    protected final static MarkWordOffset LOCK_OFFSET = MarkWordOffset.builder().hexLen("3").shiftRight(0).name("lock").build();
    protected final static MarkWordOffset BIASED_LOCK_OFFSET = MarkWordOffset.builder().hexLen("1").shiftRight(2).name("biased_lock").build();
    protected final static MarkWordOffset AGE_OFFSET = MarkWordOffset.builder().hexLen("f").shiftRight(3).name("age").build();
    protected final static MarkWordOffset HASHCODE_OFFSET = MarkWordOffset.builder().hexLen("ffffffff").shiftRight(8).name("hashcode").build();
    protected final static MarkWordOffset EPOCH_OFFSET = MarkWordOffset.builder().hexLen("3").shiftRight(7).name("epoch").build();
    protected final static MarkWordOffset THREAD_OFFSET = MarkWordOffset.builder().hexLen("7FFFFFFFFFFFFF").shiftRight(10).name("epoch").build();

    public Object instance;

    public MarkWordStrategyAbs(Object instance) {
        this.instance = instance;

        VirtualMachine vm = VM.current();
        int headerSize = vm.objectHeaderSize();
        String details = vm.details();
        System.out.println(details);

        System.out.println("虚拟机 对象头长度：" + headerSize + "\t启用指针压缩：" + (headerSize == 12));

    }

    /**
     * 输出类型信息
     *
     * @param markWords
     */
    protected abstract void printType(List<MarkWordStrategyAbs.MarkWord> markWords) throws InterruptedException;

    protected List<MarkWord> getMarkWords() {

        VirtualMachine vm = VM.current();

        int headerSize = vm.objectHeaderSize();


        List<MarkWord> list = new ArrayList<>();
        for (long off = 0; off < headerSize; off += 4) {

            int word = vm.getInt(instance, off);

            String[] wordArray = new String[4];

            // 每8位 截取一次

            wordArray[0] = binaryToDecimal((word >> 0) & 0xFF, 7);
            wordArray[1] = binaryToDecimal((word >> 8) & 0xFF, 7);
            wordArray[2] = binaryToDecimal((word >> 16) & 0xFF, 7);
            wordArray[3] = binaryToDecimal((word >> 24) & 0xFF, 7);

            list.add(MarkWord.builder().word(word).list(wordArray).build());

        }


        return list;
    }


    protected String binaryToDecimal(int n, Integer len) {

        StringBuilder sb = new StringBuilder();

        for (int i = len; i >= 0; i--) {
            sb.append(n >>> i & 1);
        }
        return sb.toString();
    }

    protected String binaryToDecimal(BigInteger n, Integer len) {

        StringBuilder sb = new StringBuilder();

        for (int i = len; i >= 0; i--) {
            sb.append(n.shiftRight(i).and(new BigInteger("1")));
        }
        return sb.toString();
    }


    /**
     * 容易阅读模式
     */
    protected String printEasyRead(List<MarkWord> markWords) {

        String markWordBinary = getEasyRead(markWords);

        System.out.println("\nmark word易读模式(从右往左)：" + markWordBinary);

        return markWordBinary;
    }

    protected String getEasyRead(List<MarkWord> markWords) {
        return markWords.stream().map(e -> {
            // 反转
            String str = Arrays.stream(e.getList()).reduce((l, r) -> r + " " + l).get();
            return str;

        }).reduce((l, r) -> r + " " + l).get();
    }

    /**
     * 输出
     *
     * @param markWords
     */
    protected void printMarkWord(List<MarkWord> markWords) {

        for (MarkWord markWord : markWords) {
            Integer word = markWord.getWord();
            String binaryToDecimal = binaryToDecimal(word, 31);
            System.out.println(word + "\t\t16进制:" + Long.toHexString(word) + "\t\t 二进制：" + binaryToDecimal);
        }

    }

    public String findOffsetCode(BigInteger easyReadBigInteger, MarkWordOffset markWordOffset) {
        String hexLen = markWordOffset.getHexLen();
        String len = new BigInteger(hexLen, 16).toString(2);
        BigInteger lock = easyReadBigInteger.shiftRight(markWordOffset.getShiftRight()).and(new BigInteger(markWordOffset.getHexLen(), 16));
        return binaryToDecimal(lock, len.length() - 1);

    }

    /**
     * 输出 对象 实例
     */
    protected void printLayout() {
        System.out.println("\n" + ClassLayout.parseInstance(instance).toPrintable());
    }

    @Data
    @Builder
    public static class MarkWord {
        // 二进制数据
        String[] list;
        // 10进制数据
        Integer word;
    }

    /**
     * 偏移信息
     */
    @Builder
    @Data
    public static class MarkWordOffset {
        //右移
        private Integer shiftRight;
        //长度
        private String hexLen;
        // 名字
        private String name;
    }
}
